#include "deepsort/op_cosinesimilarity.h"

#include <fstream>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
AtlasError OpCosineSimilarity::Init() {
    CreateOpDesc();
    // 之前犯了写Java的老毛病，直接在这个函数里创建了一个OperatorDesc对象传给了OpRunner构造方法，导致opRunner_在Init时找得
    // 到opDesc_，一旦退出本函数，栈空间上的对象就会被销毁，从而使得opRunner_在使用opDesc_时报空指针异常

    opRunner_ = new OpRunner(opDesc_);
    if (opRunner_->Init() != SUCCESS) {
        ERROR_LOG("Init OpRunner failed");
        return ATLAS_ERROR;
    }
    return ATLAS_OK;
}


OpCosineSimilarity::~OpCosineSimilarity() {
    delete opRunner_;
    delete opDesc_;
}

void OpCosineSimilarity::CreateOpDesc()
{
    // define operator
    std::vector<int64_t> shape{1, 512};
    std::string opType = "CosineSimilarity";
    aclDataType dataType = ACL_FLOAT;
    aclFormat format = ACL_FORMAT_ND;

    opDesc_ = new OperatorDesc(opType);
    opDesc_->AddInputTensorDesc(dataType, shape.size(), shape.data(), format);
    opDesc_->AddInputTensorDesc(dataType, shape.size(), shape.data(), format);
    opDesc_->AddOutputTensorDesc(dataType, shape.size(), shape.data(), format);
    opDesc_->SetAttr<int>("dim", 1);
}

Result OpCosineSimilarity::RunCosineSimilarityOp(vector<void*>& input, vector<size_t>& inputSize, vector<void*>& output) {
    // Input
    Result ret = SetInputData(input, inputSize);
    if (ret != SUCCESS) {
        ERROR_LOG("SetInputData failed when Run op");
        return FAILED;
    }
    
    // Excute
    if (opRunner_->RunOp() != SUCCESS) {   // 3~4ms for excute op
          ERROR_LOG("Run op failed");
          return FAILED;
    }
    // INFO_LOG("Run op success");

    // Output Process
    for (size_t i = 0; i < opRunner_->NumOutputs(); ++i) {
        output.emplace_back(opRunner_->GetOutputBuffer<float>(i));
        // INFO_LOG("Output[%zu]:", i);
        // opRunner_->PrintOutput(i);
    }

    return SUCCESS;
}

Result OpCosineSimilarity::SetInputData(vector<void*>& input, vector<size_t>& inputSize) {
    for (size_t i = 0; i < opRunner_->NumInputs(); ++i) {

        // 本地文件bin输入测试时使用*************
        // size_t fileSize = 0;
        // std::string filePath = "test_data/data/input_" + std::to_string(i) + ".bin";
        // bool result = ReadFile(filePath, fileSize,
        //     opRunner_->GetInputBuffer<void>(i), opRunner_->GetInputSize(i));
        
        // if (!result) {
        //     ERROR_LOG("Read input[%zu] failed", i);
        //     return FAILED;
        // }
        // INFO_LOG("Set input[%zu] from %s success.", i, filePath.c_str());
        // INFO_LOG("Input[%zu]:", i);
        // opRunner_->PrintInput(i);
        // *************************************

        if (inputSize[i] > opRunner_->GetInputSize(i)) {
            ERROR_LOG("input size is larger than buffer size");
            return FAILED;
        }
        memcpy(opRunner_->GetInputBuffer<float>(i), input[i], inputSize[i]);

        // INFO_LOG("Input[%zu]:", i);
        // opRunner_->PrintInput(i);
    }
    return SUCCESS;
}

bool ReadFile(const std::string &filePath, size_t &fileSize, void *buffer, size_t bufferSize)
{
    struct stat sBuf;
    int fileStatus = stat(filePath.data(), &sBuf);
    if (fileStatus == -1) {
        ERROR_LOG("failed to get file");
        return false;
    }
    if (S_ISREG(sBuf.st_mode) == 0) {
        ERROR_LOG("%s is not a file, please enter a file", filePath.c_str());
        return false;
    }

    std::ifstream file;
    file.open(filePath, std::ios::binary);
    if (!file.is_open()) {
        ERROR_LOG("Open file failed. path = %s", filePath.c_str());
        return false;
    }

    std::filebuf *buf = file.rdbuf();
    size_t size = buf->pubseekoff(0, std::ios::end, std::ios::in);
    if (size == 0) {
        ERROR_LOG("file size is 0");
        file.close();
        return false;
    }
    if (size > bufferSize) {
        ERROR_LOG("file size is larger than buffer size");
        file.close();
        return false;
    }
    buf->pubseekpos(0, std::ios::in);
    buf->sgetn(static_cast<char *>(buffer), size);
    fileSize = size;
    file.close();
    return true;
}

bool WriteFile(const std::string &filePath, const void *buffer, size_t size)
{
    if (buffer == nullptr) {
        ERROR_LOG("Write file failed. buffer is nullptr");
        return false;
    }

    int fd = open(filePath.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWRITE);
    if (fd < 0) {
        ERROR_LOG("Open file failed. path = %s", filePath.c_str());
        return false;
    }

    size_t writeSize = write(fd, buffer, size);
    (void) close(fd);
    if (writeSize != size) {
        ERROR_LOG("Write file Failed.");
        return false;
    }

    return true;
}